package com.bjy.qa;

import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    /**
     * 配置全局事务的切入点表达式，指定哪些包中的类使用事务，设置为静态类常量
     *
     * public: 表示public 级别方法。 可以不写，不写就是所有的方法（public, private, protected）
     * *：表示方法返回值的类型 * 表示全部类型
     * com.bjy.qa.service..：匹配 com.bjy.qa.service 包及子包下的所有类
     * ..：表示service包及子包下的所有类的所有方法
     * *：表示全部
     * .*(..)：表示全部方法
     */
    private static final String AOP_POINTCUT_EXPRESSION = "execution (public * com.bjy.qa.service..*.*(..)) && " +
            "!execution(* com.bjy.qa.service.manage.impl.CleanDataService.*(..))";

    /**
     * 事务传播机制，为哪些修改方法开启事务
     * *：表示全部方法
     * 例：private static final String[] REQUIRED_RULE_TRANSACTION = {"insert*", "create*", "add*", "save*", "modify*", "update*", "del*", "delete*"};
     */
    private static final String[] REQUIRED_RULE_TRANSACTION = {"*"};

    /**
     * 事务传播机制，为哪些查询方法开启事务
     * *：表示全部方法
     * 例：private static final String[] READ_RULE_TRANSACTION = {"select*", "get*", "query*", "search*", "count*","detail*", "find*"};
     */
    private static final String[] READ_RULE_TRANSACTION = {};

    @Resource
    private TransactionManager transactionManager; // 注入事务管理器

    /**
     * 配置事务拦截器
     */
    @Bean
    public TransactionInterceptor txAdvice() {
        RuleBasedTransactionAttribute txAttrRequired = new RuleBasedTransactionAttribute();
        txAttrRequired.setName("REQUIRED事务");
        txAttrRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 设置事务传播机制，默认是PROPAGATION_REQUIRED：如果当前存在事务，则加入该事务；如果当前没有事务，则创建一个新的事务
        txAttrRequired.setTimeout(30); // 设置事物超时时间为 30S

        //设置异常回滚为Exception  默认是RuntimeException
        List rollbackRuleAttributes = new ArrayList<>();
        rollbackRuleAttributes.add(new RollbackRuleAttribute(Exception.class));
        txAttrRequired.setRollbackRules(rollbackRuleAttributes);

        RuleBasedTransactionAttribute txAttrRequiredReadOnly = new RuleBasedTransactionAttribute();
        txAttrRequiredReadOnly.setName("SUPPORTS事务");
        txAttrRequiredReadOnly.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS); //设置事务传播机制，PROPAGATION_SUPPORTS：如果当前存在事务，则加入该事务；如果当前没有事务，则以非事务的方式继续运行
        txAttrRequiredReadOnly.setRollbackRules(rollbackRuleAttributes); // 设置异常回滚为Exception  默认是RuntimeException
        txAttrRequiredReadOnly.setReadOnly(true);
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource(); // 事务管理规则，声明具备事务管理的方法名

        // 设置事务传播机制，为哪些修改方法开启事务
        for (String s : REQUIRED_RULE_TRANSACTION) {
            source.addTransactionalMethod(s, txAttrRequired);
        }

        // 设置事务传播机制，为哪些查询方法开启事务（当查询多个数据时需要，已查询出来的数据刚好被改变的情况）
        for (String s : READ_RULE_TRANSACTION) {
            source.addTransactionalMethod(s, txAttrRequired);
        }
        return new TransactionInterceptor(transactionManager, source);
    }

    /**
     *  设置切面
     */
    @Bean
    public Advisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}
